【转载】Unity 您所在的位置:网站首页 commandbuffer drawrenderer 【转载】Unity

【转载】Unity

2023-03-09 18:26| 来源: 网络整理| 查看: 265

原文链接

版权声明:本文为CSDN博主「贤愚」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/qq_38275140…

正文 CommandBuffer

CommandBuffers 是预先写好一系列的渲染指令,然后在摄像机渲染时可以在适当的时机插入这一段指令,总之还是先看一下官方的案例

第一个是 毛玻璃 的案例,commandBuffers 的任务就是为毛玻璃材质的 shader 提供一张模糊后的屏幕截图,类似 GrabPass

下面放的是我为了方便理解 删减过的 代码,不过用的 shader 仍然是案例的 shader,一个用于 高斯模糊 的shader

[ExecuteInEditMode] public class CommandBuffer4BlurGlass : MonoBehaviour we{ /// @note 为要实现毛玻璃效果的 shader 提供一张模糊后的屏幕贴图 public Shader BlurShader; private CommandBuffer _commandBuffer; private Material _blurMat; private void OnEnable() { /// @note 创建 CommandBuffer _commandBuffer = new CommandBuffer(); _commandBuffer.name = "my grab blur"; /// @note 检查材质 if (_blurMat == null) { if (BlurShader == null) return; _blurMat = new Material(BlurShader); _blurMat.hideFlags = HideFlags.HideAndDontSave; } /// @note 申请一张临时的 texture 存放屏幕截图 -1, -1 分别表示摄像机的像素宽高 /// GetTemporaryRT 会申请 texture 的同时,会把他设为一个全局的 shader 属性, command 也可以通过这个来寻找 texture /// 所以用 Shader.PropertyToID 得到一个唯一标识符来传入 /// 你也可以用 _ScreenCopyTexture 在 shader 得到这张 texture(没试验过,应该是。。。),不过这里好像用不到 int grabScreenID = Shader.PropertyToID("_ScreenCopyTexture"); _commandBuffer.GetTemporaryRT(grabScreenID, -1, -1, 0, FilterMode.Bilinear); /// 从 BuiltinRenderTextureType.CurrentActive 得到屏幕截图 /// 这里第二个参数是 RenderTargetIdentifier 类型,但也可以用 RenderTexture 之类的进行隐式转换 _commandBuffer.Blit(BuiltinRenderTextureType.CurrentActive, grabScreenID); /// 申请两张小一点的贴图用来来回倒 int blurredID = Shader.PropertyToID("_Temp1"); int blurredID2 = Shader.PropertyToID("_Temp2"); /// 这里长宽 -2 我也不知道是什么意思。。大概是屏幕长宽 1/2 吧 _commandBuffer.GetTemporaryRT (blurredID, -2, -2, 0, FilterMode.Bilinear); _commandBuffer.GetTemporaryRT (blurredID2, -2, -2, 0, FilterMode.Bilinear); /// 这里进行降采样的操作,把大的 texture 转到小一点的 texture 里,减少计算模糊的性能消耗 _commandBuffer.Blit(grabScreenID, blurredID); /// grabScreenID 没用了就释放掉 _commandBuffer.ReleaseTemporaryRT(grabScreenID); /// @note 对图像进行高斯模糊 /// horizontal blur _commandBuffer.SetGlobalVector("offsets", new Vector4(2.0f/Screen.width,0,0,0)); _commandBuffer.Blit (blurredID, blurredID2, _blurMat); /// vertical blur _commandBuffer.SetGlobalVector("offsets", new Vector4(0,2.0f/Screen.height,0,0)); _commandBuffer.Blit (blurredID2, blurredID, _blurMat); /// horizontal blur _commandBuffer.SetGlobalVector("offsets", new Vector4(4.0f/Screen.width,0,0,0)); _commandBuffer.Blit (blurredID, blurredID2, _blurMat); /// vertical blur _commandBuffer.SetGlobalVector("offsets", new Vector4(0,4.0f/Screen.height,0,0)); _commandBuffer.Blit (blurredID2, blurredID, _blurMat); /// 最后将得到的贴图设为全局贴图,这样物体的 shader 就可以得到这张贴图了 /// (PS: 不是这里的 BlurShader, BlurShader 只用于对截图进行高斯模糊) _commandBuffer.SetGlobalTexture("_GrabBlurTexture", blurredID); _commandBuffer.ReleaseTemporaryRT(blurredID2); _commandBuffer.ReleaseTemporaryRT(blurredID); /// 最后将这个 command 加到摄像机上 Camera.main.AddCommandBuffer(CameraEvent.AfterSkybox, _commandBuffer); } private void OnDisable() { Clean(); } void Clean() { /// 在摄像机上移除该 CommandBuffer Camera.main.RemoveCommandBuffer(CameraEvent.AfterSkybox, _commandBuffer); DestroyImmediate (_blurMat); } } 复制代码

把这个挂在在一个空物体上,你会发现你的 Camera 界面多了一条信息

image.png

打开 FrameDebug 你可以看到多了一个部分

image.png

我们看到多了一个叫做 CommanBuffer.AfterSkyBox 的部分里出现了我们添加的 my grab blur ,里面的六个 Draw Dynamic 就是我们在代码里给 Commanbuffer 添加的 6 个 Blit 命令。

至于为什么在 AfterSkyBox 里,则是这段代码决定的。

Camera.main.AddCommandBuffer (CameraEvent.AfterSkybox, _commandBuffer); 复制代码

CameraEvent 里有许多的枚举,都是很直接的名字,可以看下面这张图了解不同枚举代表的时机

image.png

观察这个,我们选择 AfterSkyBox 的原因就很明显了,此时所有的 不透明物体 和 天空盒 都渲染好了,而 透明物体 还没开始渲染,我们的毛玻璃就是一个透明物体,所以 CommandBuffer 执行结束后,因为 commandBuffer.SetGlobalTexture("_GrabBlurTexture", blurredID); 这个指令,毛玻璃 shader 就可以通过 _GrabBlurTexture 得到他想要的模糊屏幕截图了

这里 Blit() 传入的参数都是 int 类型的 id,但他的实际类型是 RenderTargetIdentifier,只不过他支持 int 还有 RenderTexture 类型的隐式转换。

Unity 原文这样,应该没理解错

Render texture to use can be indicated in several ways: a RenderTexture object, a temporary render texture created with GetTemporaryRT, or one of built-in temporary textures (BuiltinRenderTextureType). All that is expressed by a RenderTargetIdentifier struct, which has implicit conversion operators to save on typing.

所以你是通过 RenderTexture.GetTemporary 之类的方式得到的 RenderTexture 或其他相关的类型,也是可以直接当参数传进去,就像用 Graphics.Blit() 一样。

剩下的两个例子都是用在 延迟渲染 模式下的,而且我感觉重点好像都是在 Shader 里面,一个是实现 自定义的光照 一个是 类似投影 的效果,由于比较复杂(不懂),我就没有自己写一遍。

看了一下官方的代码,在 CommandBuffer 方面其实主要就是用了 CommandBuffer.DrawMesh() 这个指令,顾名思义,就是渲染一个物体。和 DrawMesh 类似的还有一个 DrawRenderer 方法,可以根据自己的需要选择

除此之外,在第三个案例里还用了 SetRenderTarget() 的方法,

buf.SetRenderTarget (BuiltinRenderTextureType.GBuffer0, BuiltinRenderTextureType.CameraTarget); foreach (var decal in system.m_DecalsDiffuse) { // 这里的 shader 返回的是 diffuse buf.DrawMesh (m_CubeMesh, decal.transform.localToWorldMatrix, decal.m_Material); } // render normals-only decals into normals channel buf.SetRenderTarget (BuiltinRenderTextureType.GBuffer2, BuiltinRenderTextureType.CameraTarget); foreach (var decal in system.m_DecalsNormals) { // 这里的 shader 返回的是 normal buf.DrawMesh (m_CubeMesh, decal.transform.localToWorldMatrix, decal.m_Material); } // render diffuse+normals decals into two MRTs RenderTargetIdentifier[] mrt = {BuiltinRenderTextureType.GBuffer0, BuiltinRenderTextureType.GBuffer2}; buf.SetRenderTarget (mrt, BuiltinRenderTextureType.CameraTarget); foreach (var decal in system.m_DecalsBoth) { // 这里的 shader 有两个 out 类型,一个 diffuse,一个 normal buf.DrawMesh (m_CubeMesh, decal.transform.localToWorldMatrix, decal.m_Material); } 复制代码

前两个参数一般是 颜色 和 深度,

深度不要管,颜色我粗浅估计就是 shader 中的片元函数的返回值,所以 SetRenderTarget 就是 决定 shader 里算出来的值应该被存储在哪里,因为这里是 延迟渲染 模式,所以会 先算几何信息再算光照,而所有的几何信息信息的存贮在一个叫 GBuffer 的东西里,

其中 GBuffer0 存储的是漫射信息 Diffuse, GBuffer2 存储的是则是法线信息 Normal(更多可以看这个),

所以通过 SetRenderTarget 就可以指定将得到的信息存储到对应的信息区域,第三个例子共提供了三个 shader,

NormalOnly 这个 shader 返回的实际是法线信息,所以要把它指向 GBuffer2

return fixed4(nor*0.5+0.5,1); 复制代码

而 DiffuseNormal 这个 shader 则是通过两个 out 参数来传递 Diffuse 和 Normal 信息

void frag(v2f i, out half4 outDiffuse : COLOR0, out half4 outNormal : COLOR1) 复制代码

所以 SetRenderTarget 第一个参数指定的是包含 GBuffer0、 GBuffer2 的数组。

CommandBuffer 的其他方法

这部分涉及到 ComputeShader 不了解,先跳过

ComputeShader 就是可称为 GPGPU 编程的东西。顾名思义,它是一段 shader 程序。它有 shader 的特性,比如是在 GPU 上运行的,但它又是脱离于正常的渲染管线之外的,即不对渲染结果造成影响。这跟我们普通的 shader 不同。

这种 shader 扩展名为 .compute , 是 DX11 的 HLSL 风格的 shader,当然,也可以转为 GLSL,使其能兼容 OpenGL (目前还没测试,只在 DX11 上测试过)

详情参考 Unity 中的 ComputeShader

设置各种 数值 相关的参数

image.png

这部分就是设置各种 全局可访问 的数据

image.png

这部分可以设置 摄像头相关的多种参数,像修改摄像机的各种变换矩阵啦、视锥体啦

image.png

去掉这三大块,剩下就没多少了,有些方法涉及的概念太高级了,放弃理解

PostProcessing

PostProcessing 是 Unity 提供的一个后处理的插件, 之前只会用 OnRenderImage() 实现一些简单屏幕后处理效果,之所以把它和 CommandBuffer 放在一块,是因为我发现它实现后处理效果用的也是 CommandBuffer。

image.png

用法

首先在目标摄像头挂载一个 PostProcessLayer 脚本;

image.png

Anti-aliasing 就是反锯齿效果,如果摄像机是 延迟渲染 模式,还会有个 DeferredFog Layer 表示 后处理所在的层,也就是说 PostProcessVolume 所在的 layer 没有被包括,那么 PostProcessVolume 上的后处理效果就不会生效

右键生成一个 Post-Process Volume;

image.png

image.png

它还需要一个 Profile 文件,Profile 就是一个序列化的文件,存储了你想要实现的屏幕特效,一开始是空的,要点 New 生成一个,然后就可以在 Asset 面板里看到这样的一个资源文件了;

image.png

你可以在这里增减特效,当然也可以直接在 Post-Process Volume 里调整,Post-Process Volume 把这个资源读取并显示在 Inspector 上了;

它还有一个 Box Collider,如名字里的 Volume,他可以设置一个区域,只要当摄像机进入该区域,这个 PostProcess 才会生效,如果你想让这个 PostProcess 任何地方都有,就把 Is Global 点上;

点 Add effect 就可以添加特效了。

总共有有 11 个内置的后处理效果,自己试一下就知道是干嘛的了,也可以看这个

环境光遮蔽 屏幕空间反射 动态模糊 自动曝光(模拟人眼对光线的适应,)Auto Exposure Bloom 径向的色彩偏移 Chromatic Aberration 调色滤镜 Color Grading 景深 Depth of Field 胶片纤维 Grain 扭曲 Lens Distortion 遮罩 Vignette 特定时机生成

如果你想在游戏进行的 每个特定的时机生成一个特效,用 QuickVolume() 方法

public class VignettePulse : MonoBehaviour { PostProcessVolume m_Volume; Vignette m_Vignette; void Start() { m_Vignette = ScriptableObject.CreateInstance(); m_Vignette.enabled.Override(true); m_Vignette.intensity.Override(1f); m_Volume = PostProcessManager.instance.QuickVolume(gameObject.layer, 100f, m_Vignette); } void Update() { m_Vignette.intensity.value = Mathf.Sin(Time.realtimeSinceStartup); } void OnDestroy() { RuntimeUtilities.DestroyVolume(m_Volume, true, true); } } 复制代码 自定义屏幕特效

另外也可以写自定义屏幕特效

不过 Shader 要用 HLSL(考虑到兼容性)

C# 部分需要两个类 分别继承 PostProcessEffectSettings 和 PostProcessEffectRenderer

这里实现一个 灰阶 的屏幕特效

using System; using UnityEngine; using UnityEngine.Rendering.PostProcessing; [Serializable] [PostProcess(typeof(GrayscaleRenderer), PostProcessEvent.AfterStack, "Custom/Grayscale")] public sealed class Grayscale : PostProcessEffectSettings { [Range(0f, 1f), Tooltip("Grayscale effect intensity.")] public FloatParameter blend = new FloatParameter { value = 0.5f }; } public sealed class GrayscaleRenderer : PostProcessEffectRenderer { public override void Render(PostProcessRenderContext context) { var sheet = context.propertySheets.Get(Shader.Find("Hidden/Custom/Grayscale")); sheet.properties.SetFloat("_Blend", settings.blend); context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); } } 复制代码

继承 PostProcessEffectSettings 的类是 收集数据 的,也就是显示在 Inspector 面板上的各种属性,所以脚本的名字也应该和这个类名一致。

image.png

因为 Post-process Volume Profile 是可序列化的文件,所以附加在上面的 PE 自然是要可以序列化的,

要加 [Serializable] 特性, 然后是这个特性 [PostProcess(typeof(GrayscaleRenderer), PostProcessEvent.AfterStack, "Custom/Grayscale")]

第一个参数 指定真正实现渲染特效的 继承 PostProcessEffectRenderer 的类。

第二个参数 表示在一系列特效中,他的 渲染顺序,有三个选择

BeforeTransparent: 该效果只会在 透明 pass 完成之 前 应用到 不透明 对象上。 BeforeStack: 该效果将在 内置堆栈 启动之 前 应用。这包括抗锯齿、景深、色调映射等。 AfterStack: 该效果将应用于 内置堆栈 之 后,FXAA (如果启用)和 final-pass 抖动 之 前。

最后一个参数 就是他的 选择路径

image.png

在 GrayscaleRenderer 重写 Render() 方法实现渲染的逻辑。

除了 Render(),还有 Init(), Release() 等可以重写

附上 Shader

这些代码都是官方的,用的是 HLSL 的代码块,而不是 CG 的

在 /PostProcessing/Shaders/API/ 下有各个平台的 Unity 提供的 Shader 的 API

Shader "Hidden/Custom/Grayscale" { HLSLINCLUDE #include "PostProcessing/Shaders/StdLib.hlsl" TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); float _Blend; float4 Frag(VaryingsDefault i) : SV_Target { float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); float luminance = dot(color.rgb, float3(0.2126729, 0.7151522, 0.0721750)); color.rgb = lerp(color.rgb, luminance.xxx, _Blend.xxx); return color; } ENDHLSL SubShader { Cull Off ZWrite Off ZTest Always Pass { HLSLPROGRAM #pragma vertex VertDefault #pragma fragment Frag ENDHLSL } } } 复制代码


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有